
const dgram = require('dgram');
const { DataManager } = require("./data_manager.js");
const { CLIENT_IP, CLIENT_PORT, LINE_INTERVAL_TIME } = require("../config");


const client = dgram.createSocket('udp4');
client.on('message', function (msg, rinfo) {
    console.log('已接收到服务器端发送的数据', msg);
    console.log('服务器地址为', rinfo.address);
    console.log('服务器所用端口号为', rinfo.port);
});
client.on('error', function (err) { console.log(`[client-error]:${err.message}`); });


//  ===程序运行变量===
//  比较时间, 用来控制发包频率时做为对比时间, (当前时间-比较时间 >= 间隔) 时就可以发送下一个包
let compare_time = 0;
//  程序工作状态, false:未工作, true:正在工作
let work_status = false;

//  +++调试用到的变量+++
//  发送文件计数
let file_count = 0;
//  调试模拟的 包编号计数
let compare_temp_id = -1;
//  调试模拟的 分包编号计数
let compare_packet_id = -1;


/**
 * 通过udp推送一个数据包
 * @param {Buffer} packet 要发送的数据包
 * @return {Promise<String>} 发送结果描述
 */
async function send_packet(packet) {
    // //  打印发送的 包序号 + 分包编号
    // let data = packet.toString('hex');
    // const packet_id = parseInt(data.substr(10, 2), 16);
    // const temp_id   = parseInt(data.substr(6, 4), 16);
    // console.log(`temp_id:${temp_id}, packet_id:${packet_id}`);

    //  使用Promise包一层是为了控制每个包发送的顺序, 一个数据包发送完成后才开始下一个数据包发送
    return new Promise((res, rej) => {
        client.send(packet, 0, packet.length, CLIENT_PORT, CLIENT_IP, function (err, bytes) {
            if (err) {
                console.log(`[数据包发送失败]: ${err.message}`);
                res('发送数据失败');
            } else {
                res('发送数据成功');
            }
        });
    })
}


/**
 * 开启推送报文数据服务
 * @description 
 * 1. DataManager.open() 打开数据管理对象
 * 2. 开启while(true)死循环, 每循环1次 DataManager.read()读取一个文件的数据并逐行发送
 * 3. 每行数据发送的频率需要通过绝对时间进行控制(每1行发送时间控制为1ms)
 */
async function start_data_push() {
    //  如果处在工作状态, 则不需要重复启动
    if (work_status == true) {
        return
    }

    //  打开数据操作对象
    let result = await DataManager.open();
    if (result == false) {
        console.log("子程序启动超时或出错");
        return false;
    }
    console.log("子程序启动就绪");

    //  子程序就绪时记录精准时间
    compare_time = process.hrtime.bigint();
    //  更新工作状态, true:开始工作
    work_status = true;

    //  循环发送数据
    while (work_status) {
        //  读取一个文件的数据, data:一个文件的数据 
        /**
         * [ -------------------- 1个文件的数据
         *   [ ------------------ 1行的数据
         *     "领袖包", --------- 应该是Buffer类型,但父子进程传输过程中发生了自动类型转换,所以发送时需要Buffer.form()转换一下类型
         *     "数据包1",
         *     "数据包2",
         *     ...
         *     "数据包n"
         *   ]
         * ]
         */

        //  +++调试信息+++
        //  用来记录发送1个文件所用时间的比较值
        let file_time1 = process.hrtime.bigint();

        //  读1个文件的数据
        let data = await DataManager.read();
        //  +++调试信息+++
        //  打印每个文件数据读取所用的时间
        console.log(`[${++file_count}] 读取文件用时:${(process.hrtime.bigint() - file_time1) / BigInt(1000000)}ms`);

        if (data) {
            //  row:每一行数据  
            //  数据格式: [ "领袖包", "数据包1", "数据包2", ..., "数据包n" ]
            for (let row of data) {
                //  +++调试信息+++
                //  用来记录发送1行数据所用时间的比较直
                let line_time1 = process.hrtime.bigint();

                //  发送一行数据的频率控制在1ms
                if ((process.hrtime.bigint() - compare_time) < LINE_INTERVAL_TIME) {
                    await waiting_time(LINE_INTERVAL_TIME - (process.hrtime.bigint() - compare_time));
                }

                //  +++调试信息+++
                //  模拟的包编号,用来对比格式化数据的包需要是否符合顺序
                if ((++compare_temp_id) > 65535) { compare_temp_id = 0; }
                //  模拟的分包编号,用来对比格式化数据的包需要是否符合顺序
                compare_packet_id = -1;

                //  发送一行数据的包, 一行数据包含多个数据包
                for (let packet of row) {
                    //  因为父子进程传输中Buffer数据发生了自动类型转换, 所以需要转换一下(转回Buffer)
                    await send_packet(Buffer.from(packet));
                }

                //  +++调试信息+++
                // console.log(`发送一行数据用时: ${(process.hrtime.bigint()-line_time1) / BigInt(1000)}vs`);
                //  更新下一行数据包的发送时间
                compare_time += BigInt(LINE_INTERVAL_TIME);
            }
        } else {
            console.log(`读取到了一个空文件`);
        }
        //  +++调试信息+++
        //  打印发送1个文件所花费的时间(ms)
        console.log(`[${file_count}] 发送文件用时:${(process.hrtime.bigint() - file_time1) / BigInt(1000000)}ms`);
    }
}


/**
 * 结束推送报文数据服务
 * @description
 * 1. 关闭数据管理对象
 * 2. 更新程序运行状态, 设置为 false:停止工作
 */
async function stop_data_push() {
    //  关闭数据管理对象
    let result = await DataManager.close()
    if (result == true) {
        //  更新工作状态, false:停止工作
        work_status = false;
        console.log(`[工作正常停止]`);
    }
}


/**
 * 延时等待一段时间(ns), 并不精准,因为setImmediate实现,所以队列之间切换的时间并不固定(最小500vs)
 * @param {Number} time 等待的时长(ns);
 */
async function waiting_time(time = 10) {
    let time1 = process.hrtime.bigint();
    //  如果时间未到, setImmediate等待一会儿再判断时间
    while (process.hrtime.bigint() - time1 < time) {
        await new Promise((res, rej) => setImmediate(res));
    }
}





module.exports = {
    start_data_push,
    stop_data_push,
}
